package com.hopelic.canal.database.sync;


import cn.cz.common.canal.sync.handle.CanalColumnDTO;
import cn.cz.common.canal.sync.handle.CanalDTO;
import cn.cz.common.canal.sync.handle.ICanalTableHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

import java.sql.Connection;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Slf4j
@Component
public class DefaultCanalTableHandler implements ICanalTableHandler {

   private static final Pattern COMPILE = Pattern.compile("(\'|\")");
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public boolean isSupport(String schemaName, String tableName) {
        return true;
    }

    @Override
    public void handlerUpdate(CanalDTO canalDTO) {
        updateColumn(jdbcTemplate, canalDTO.getSchemaName(), canalDTO.getTableName(), canalDTO.getBeforeColumns(),canalDTO.getAfterColumns());
    }

    @Override
    public void handlerInsert(CanalDTO canalDTO) {
        insertColumn(jdbcTemplate, canalDTO.getSchemaName(),canalDTO.getTableName(),canalDTO.getAfterColumns() );
    }

    @Override
    public void handlerDelete(CanalDTO canalDTO) {
        delColumn(jdbcTemplate, canalDTO.getSchemaName(),canalDTO.getTableName(),canalDTO.getBeforeColumns());
    }

    private void delColumn(JdbcTemplate jdbcTemplate, final String schemaName, final String tableName, List<CanalColumnDTO> columns) {
        final String condition = getWhere(columns);
        final String sql = String.format("DELETE from   `%s`.`%s`  where  %s", schemaName, tableName, condition);
        jdbcTemplate.execute((Connection con) -> {
            log.info("delete_sql:{}", sql);
            con.createStatement().executeUpdate(sql);
            return true;
        });

    }



    private  void insertColumn(JdbcTemplate jdbcTemplate, final String schemaName, final String tableName, List<CanalColumnDTO> columns) {
        final String condition = columns.stream().map(t -> "`"+t.getName()+"`").collect(Collectors.joining(","));
        final String value = columns.stream().map(t -> t.isNull() ? null : "'" + grep(t.getValue()) + "'").collect(Collectors.joining(","));
        final String sql = String.format("INSERT INTO `%s`.`%s` (%s)  values(%s)", schemaName,tableName, condition, value);
        jdbcTemplate.execute((Connection con)->{
            log.info("insert_sql:{}",sql);
            con.createStatement().executeUpdate(sql);
            return true;
        });
    }

    private  void updateColumn(JdbcTemplate jdbcTemplate, final String schemaName, final String tableName, List<CanalColumnDTO> before, List<CanalColumnDTO> after) {
        final String where = getWhere(before);
        final String set = after.stream().map(t -> t.isNull() ? "`"+t.getName()+"`" + "=null" : "`"+t.getName()+"`" + "='" + grep(t.getValue()) + "'").collect(Collectors.joining(","));
        final String sql = String.format("UPDATE `%s`.`%s` set %s  where  %s",schemaName, tableName, set, where);
        jdbcTemplate.execute((Connection con) -> {
            log.info("update_sql:{}", sql);
            final int i = con.createStatement().executeUpdate(sql);
            return true;
        });
    }

    private  String getWhere(List<CanalColumnDTO> columns) {
        final String condition = columns.stream().map(t -> {
            final boolean isNull = t.isNull();
            if (isNull) {
                return "`"+t.getName()+"`" + " is null";
            } else {
                return "`"+t.getName()+"`" + "='" + grep(t.getValue()) + "'";
            }

        }).collect(Collectors.joining(" and "));
        return condition;
    }


    private static String  grep(String s){
        final Matcher matcher = COMPILE.matcher(s);
        StringBuffer  sb=new StringBuffer();
        while (matcher.find()){
            matcher.appendReplacement(sb,"\\\\"+matcher.group(1));
        }
        matcher.appendTail(sb);
        return sb.toString();
    }


}
